using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using HybridCLR.Editor;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace GameMain.Editor.BuildPipeline
{
    public partial class BuildPipeline
    {
        private static uint GetPInvokeMethodCount()
        {
            var sw = Stopwatch.StartNew();
            var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            var allTypes = allAssemblies.SelectMany(assembly => assembly.GetTypes())
                .ToArray();
            var aotPInvoke = typeof(AOT.MonoPInvokeCallbackAttribute);
            var xLuaPInvoke = typeof(XLua.MonoPInvokeCallbackAttribute);
            uint pInvokeCount = 0;
            var bindFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
            foreach (var type in allTypes)
            {
                var allMtds = type.GetMethods(bindFlags);
                foreach (var mtd in allMtds)
                {
                    try
                    {
                        if (mtd.IsDefined(aotPInvoke) || mtd.IsDefined(xLuaPInvoke))
                        {
                            pInvokeCount++;
                        }
                    }
                    catch
                    {

                    }
                }
            }

            sw.Stop();

            Debug.Log($"[BuildPipeline:GetPInvokeMethodCount] cnt:{pInvokeCount} {sw.ElapsedMilliseconds}ms!!!");

            return pInvokeCount;
        }

        private static void GenerateReversePInvokeMethod()
        {
            var file = Path.Combine(SettingsUtil.LocalIl2CppDir, "libil2cpp", "hybridclr", "metadata",
                "ReversePInvokeMethodStub.cpp");
            file = Path.GetFullPath(file).Replace('\\', '/');
            if (File.Exists(file))
            {
                File.Delete(file);
            }

            using var fs = new FileStream(file, FileMode.Create, FileAccess.Write);

            Action<string> writeLine = _msg =>
            {
                var msg = string.IsNullOrEmpty(_msg) ? "" : _msg;
                var trueMsg = System.Text.Encoding.UTF8.GetBytes(msg + "\r\n");
                fs.Write(trueMsg, 0, trueMsg.Length);
            };

            writeLine("#include \"ReversePInvokeMethodStub.h\"");
            writeLine("#include \"MetadataModule.h\"");
            writeLine(null);
            writeLine("namespace hybridclr");
            writeLine("{");
            writeLine("\tnamespace metadata");
            writeLine("\t{");
            writeLine("\t\t//!!!{{REVERSE_PINVOKE_METHOD_STUB");
            writeLine(null);
            writeLine("\t\tvoid CallLuaFunction(void* xState, int32_t wrapperIndex)");
            writeLine("\t\t{");
            writeLine(
                "\t\t\tconst MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex(wrapperIndex);");
            writeLine("\t\t\ttypedef void (*Callback)(void* xState, const MethodInfo* method);");
            writeLine("\t\t\t((Callback)GetInterpreterDirectlyCallMethodPointer(method))(xState, method);");
            writeLine("\t\t}");
            writeLine(null);

            var cnt = (int) (GetPInvokeMethodCount() * 1.5f);

            for (var i = 0; i < cnt; ++i)
            {
                writeLine($"\t\tvoid __ReversePInvokeMethod_{i}(void* xState)");
                writeLine("\t\t{");
                writeLine($"\t\t\tCallLuaFunction(xState, {i});");
                writeLine("\t\t}");
                writeLine(null);
            }

            writeLine("\t\tIl2CppMethodPointer s_ReversePInvokeMethodStub[]");
            writeLine("\t\t{");
            for (var i = 0; i < cnt; ++i)
            {
                writeLine($"\t\t\t(Il2CppMethodPointer)__ReversePInvokeMethod_{i},");
            }

            writeLine("\t\t\tnullptr,");
            writeLine("\t\t};");
            writeLine(null);
            writeLine("\t//!!!}}REVERSE_PINVOKE_METHOD_STUB");
            writeLine("\t}");
            writeLine("}");
        }
    }
}
